/**
* AutoSWF - Cody Cushing. March 26, 2009
* Copyright (c) 2009 Cody Cushing
* No use restrictions. Maintain this notice in all versions. 
**/

package com.autoswf.locator
{
	import com.autoswf.core.ChainLink;
	import com.autoswf.core.Transport;
	
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	
	import flexunit.framework.TestCase;

	public class LocatorTest extends TestCase
	{
		private var locator:Locator;
		private var mockService1:IMockService1;
		private var mockService2:IMockService2;
		
		public function LocatorTest(methodName:String=null)
		{
			super(methodName);
		}
		
		override public function setUp():void
		{
			locator = new Locator("locatorId");
			mockService1 = new MockService1(); /* implements IMockService1 */
			mockService2 = new MockService2(); /* implements IMockService1, IMockService2 */
		}
		
		/**
		 * Tests that registered services are properly indicated. 
		 */		
		public function testHasService():void
		{
			locator.addService(mockService1, MockService1);
			locator.removeService(MockService1);
			assertFalse("service still present after removal", locator.hasService(MockService1));
		}

		public function testAddConcreteService():void
		{
			locator.addService(mockService1, MockService1);
			assertTrue("failed to indicate service", locator.hasService(MockService1));			
		}
		
		/**
		 * Tests that a concrete class can be exposed as an interface. 
		 */		
		public function testAddInterfaceService():void
		{
			locator.addService(new MockService1(), IMockService1);
			assertFalse("Registered by interface but availble by concrete", locator.hasService(MockService1));
		}
		
		/**
		 * Tests that related class definition keys dont point to wrong service.  
		 */		
		public function testPluralRegistration():void
		{
			locator.addService(mockService2, MockService2);
			assertFalse("indicated unregistered service", locator.hasService(IMockService2));
			
			locator.addService(mockService2, IMockService1);
			assertTrue("failed to indicate extended interface service", locator.hasService(IMockService1));			
		}
		
		/**
		 * Tests that locator throws an error if a service is registered by a definition it does not support.
		 */		
		public function testInvalidRegistration():void
		{
			var flag:Boolean = false;
			try
			{
				locator.addService(mockService1, IMockService2);
			}
			catch(error:Error)
			{
				flag = true;
				assertFalse("Service still registered to invalid definition", locator.hasService(IMockService1))
			}
			assertTrue("Registering service to unsupported class definition did not throw error", flag);
		}
		
		/**
		 * Tests that an error is thrown when overwriting an existing service. 
		 */		
		public function testRegistrationCollision():void
		{
			locator.addService(mockService1, IMockService1);
			
			var flag:Boolean = false;
			try
			{
				locator.addService(mockService2, IMockService1);
			}
			catch(error:IllegalOperationError)
			{
				flag = true;
			}
			assertTrue("Key collision did not throw error", flag);
		}
		
		/**
		 * Tests that a service can be registered from somewhere down the chain.
		 */		
		public function testAddServiceRemotely():void
		{
			var link:ChainLink = new ChainLink("childLink");
			link.addParentLink(locator);
			locator.addChildLink(link);
			
			var serviceRegistration:ServiceRegistration = new ServiceRegistration(mockService1, IMockService1)
			link.sendUpChain(serviceRegistration);
			
			var serviceRequest:ServiceRequest = new ServiceRequest(IMockService1);
			link.sendUpChain(new Transport(serviceRequest));
			
			assertStrictlyEquals("service reference not set in request object", mockService1, serviceRequest.result);
		}
		
		/**
		 * Tests that a reference to the service is set in the ServiceRequest when that service exists. 
		 */		
		public function testSyncronousRequest():void
		{
			locator.addService(mockService1, IMockService1);
			
			var serviceRequest:ServiceRequest = new ServiceRequest(IMockService1);
			locator.sendUpChain(new Transport(serviceRequest));
			
			assertStrictlyEquals("service reference not set in request object", mockService1, serviceRequest.result);
		}
		
		/** 
		 * Tests that the handler is invoked when the service is located.
		 */		
		public function testAsyncronousRequest():void
		{
			locator.addService(mockService1, IMockService1);
									
			var handlerInvoked:Boolean = false;
			var handler:Function = function(payload:IMockService1):void
			{
				assertStrictlyEquals("failed to return remotely added instance", mockService1, payload);
				handlerInvoked = true;
			}
			var serviceRequest:ServiceRequest = new ServiceRequest(IMockService1, handler);
			locator.sendUpChain(new Transport(serviceRequest));
			
			assertTrue("handler not invoked on asyncronous service request", handlerInvoked);
		}

		/**
		 * Tests that locator provides service to handler. 
		 */		
		public function testDirectServiceAccess():void
		{
			locator.addService(mockService1, IMockService1);
									
			var serviceRequest:ServiceRequest = new ServiceRequest(IMockService1);
			locator.sendUpChain(new Transport(serviceRequest)); /* should sendUpChain wrap requests in Transport?*/
			
			assertStrictlyEquals("service reference not set when directly requesting service", mockService1, serviceRequest.result);
		}
		
		/**
		 * Tests that locator provides service to handler when child requests it.
		 */		
		public function testChildServiceAccess():void
		{
			locator.addService(mockService1, IMockService1);
									
			var link:ChainLink = new ChainLink("desperatelySeekingFoo");
			link.addParentLink(locator);
			locator.addChildLink(link);

			var serviceRequest:ServiceRequest = new ServiceRequest(IMockService1);
			link.sendUpChain(serviceRequest);
			
			assertStrictlyEquals("service reference not set in child request object", mockService1, serviceRequest.result);
		}
		
		/**
		 * Tests that locator passes service request up the chain if it does not have the service. 
		 */		
		public function testServiceLocatorTierBubbling():void
		{
			var parentLocator:ILocator = new Locator("parentLocator");
			parentLocator.addService(mockService1, IMockService1);
			
			locator.addParentLink(parentLocator);
			parentLocator.addChildLink(locator);
									
			var link:ChainLink = new ChainLink("client");
			link.addParentLink(locator);
			locator.addChildLink(link);

			var serviceRequest:ServiceRequest = new ServiceRequest(IMockService1);
			link.sendUpChain(serviceRequest);
			
			assertStrictlyEquals("service reference not set in request object", mockService1, serviceRequest.result);
		}
		
	}
}